/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package solitaireofdoom.freecell;

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.image.*;

import solitaireofdoom.solitaire.*;

/**
 * All of the freecell piles (FreecellCellPile, FreecellFoundationPile, and FreecellTableuPile) inherit the Pile class, serve basic pile functions, and accept cards as well as discard them. They have DropTarget event listeners that are fired when a CardCanvas is dropped on them, it containsthe logic that checks if the pile can accept the cards that are being dropped.
 *
 * @author Administrator
 */
public class FreecellFoundationPile extends Pile
{
    private Freecell freecell;
    private char suit;

    private int acceptableActions = DnDConstants.ACTION_MOVE;
    private DropTarget dropTarget;
    private DropTargetListener dropTargetListener;

    public FreecellFoundationPile(BufferedImage cards)
    {
        super();

        this.cards = cards;

        this.dropTargetListener = new FoundationPileDropTargetListener();
        this.dropTarget = new DropTarget(this,
                this.acceptableActions,
                this.dropTargetListener,
                true);
    }

    public FreecellFoundationPile(char suit, Freecell freecell, BufferedImage cards)
    {
        super();

        this.cards = cards;
        this.freecell = freecell;
        this.suit = suit;

        try
        {
            this.add(new CardCanvas(new Card(this.suit, Card.DummyAceCards, true), this.cards));
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }

        this.dropTargetListener = new FoundationPileDropTargetListener();
        this.dropTarget = new DropTarget(this,
                this.acceptableActions,
                this.dropTargetListener,
                true);
    }

    /**
     * Adds a <b>CardCanvas</b> to this pile.
     * @param cc the <b>CardCanvas</b> that is supposed to be added to this pile.
     * @return returns the new <b>CardCanvas</b>
     */
    public java.awt.Component add(CardCanvas cc)
    {
        cc.pile = this;
        this.pileCards.add(cc);

        this.redraw();

        return cc;
    }

    /**
     * Removes all the cards from this pile
     */
    public void clear()
    {
        this.pileCards.clear();

        try
        {
            this.add(new CardCanvas(new Card(this.suit, 999, true), this.cards));
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }

        this.redraw();
    }

    /**
     * Shows the cards in the pile
     */
    public void redraw()
    {
        this.removeAll();

        this.pileCards.lastElement().setLocation(1, 1);
        this.add((Component)this.pileCards.lastElement());
    }

    /**
     * Moves cards to the suit piles(foundations) where applicable
     */
    public void autoMoveToSuit()
    {
        this.freecell.autoMoveToSuit();
    }

    /**
     * Reports if a card was clicked in the pile
     *
     * @param isTopMost is the card that was click on top
     */
    public void cardClicked(boolean isTopMost)
    {
        this.freecell.pileChanged(false);
    }

    public class FoundationPileDropTargetListener implements DropTargetListener
    {
        /**
         * Decides if all the output should be shown
         */
        private final boolean fullOutput = false;

        /**
         * Checks if the type of object being dragged is supported
         *
         * @param e
         * @return true if it's supported
         */
        private boolean isDragFlavorSupported(DropTargetDragEvent e)
        {
            return e.isDataFlavorSupported(DeckTransferable.localCardFlavor) ||
                    e.isDataFlavorSupported(DeckTransferable.cardFlavor);
        }

        /**
         * Returns a chosen drop flavor
         *
         * @param e
         * @return true if it's supported
         */
        private DataFlavor chooseDropFlavor(DropTargetDropEvent e)
        {
            if (e.isDataFlavorSupported(DeckTransferable.localCardFlavor))
                return DeckTransferable.localCardFlavor;
            else if (e.isDataFlavorSupported(DeckTransferable.cardFlavor))
                return DeckTransferable.cardFlavor;

            return null;
        }

        /**
         * Checks if the drag has the correct flavor and correct drag 'n drop action
         *
         * @param e
         * @return
         */
        private boolean isDragOk(DropTargetDragEvent e)
        {
            if(isDragFlavorSupported(e) == false)
            {
                if (fullOutput)
                    System.out.println( "isDragOk:no flavors chosen" );
                return false;
            }

            int dropAction = e.getDropAction();

            if (fullOutput)
            {
                System.out.print("dt drop action " + dropAction);
                System.out.println(" my acceptable actions " + acceptableActions);
            }

            return (dropAction & FreecellFoundationPile.this.acceptableActions) != 0;
        }

        /**
         * this is called when a dragged object is dragged into this pile
         *
         * @param e
         */
        public void dragEnter(DropTargetDragEvent e)
        {
            if (fullOutput)
                System.out.println("PlayingPileDropTargetListener.dragEnter()");

            if (!isDragOk(e))
            {
                if(fullOutput)
                    System.out.println("dragEnter not ok");

                e.rejectDrag();
            }
            else
            {
                if (fullOutput)
                    System.out.println("dragEnter accepting: " + e.getDropAction());

                e.acceptDrag(e.getDropAction());
            }
        }

        /**
         * this is called when a dragged object is over this pile
         *
         * @param e
         */
        public void dragOver(DropTargetDragEvent e)
        {
            if (fullOutput)
                System.out.println("PlayingPileDropTargetListener.dragOver()");
            if (!isDragOk(e))
            {
                if (fullOutput)
                    System.out.println("dragOver not ok");
                e.rejectDrag();
            }
            else
            {
                if (fullOutput)
                    System.out.println("dragOver accepting: " + e.getDropAction());
                e.acceptDrag(e.getDropAction());
            }
        }

        /**
         * this is called when the drop action changes
         *
         * @param e
         */
        public void dropActionChanged(DropTargetDragEvent e)
        {
            if (fullOutput)
                System.out.println("PlayingPileDropTargetListener.dropActionChanged()");
            if (!isDragOk(e))
            {
                if (fullOutput)
                    System.out.println("dropActionChanged not ok");
                e.rejectDrag();
            }
            else
            {
                if (fullOutput)
                    System.out.println("dropActionChanged accepting: " + e.getDropAction());
                e.acceptDrag(e.getDropAction());
            }
        }

        /**
         * this is called when a dragged object is dragged out of this pile
         *
         * @param e
         */
        public void dragExit(DropTargetEvent e)
        {
            if (fullOutput)
                System.out.println("PlayingPileDropTargetListener.dragExit()");
        }

        /**
         * this is called when a dragged object is dropped on this pile
         *
         * @param e
         */
        public void drop(DropTargetDropEvent e)
        {
            if (fullOutput)
                System.out.println("PlayingPileDropTargetListener.drop()");

            // Check the chosen drop flavor
            DataFlavor chosen = chooseDropFlavor(e);
            if (chosen == null)
            {
                System.err.println("No flavour match found");
                e.rejectDrop();
                return;
            }

            int dropAction = e.getDropAction();
            int sourceAction = e.getSourceActions();

            if (fullOutput)
            {
                System.out.println("drop(), dropAction: " + dropAction);
                System.out.println("drop(), sourceAction: " + sourceAction);
            }

            // Check if the actions correspond
            if ((sourceAction & FreecellFoundationPile.this.acceptableActions) == 0)
            {
                System.err.println("No action match found!");
                e.rejectDrop();
                return;
            }

            // Try to get the dropped data
            Object data = null;
            try
            {
                e.acceptDrop(FreecellFoundationPile.this.acceptableActions);

                data = e.getTransferable().getTransferData(chosen);
                if (data == null)
                    throw new NullPointerException();
            }
            catch (Throwable t)
            {
                System.err.println("Couldn't get transfer data: " + t.getMessage());
                t.printStackTrace();
                e.dropComplete(false);
                return;
            }
            if (fullOutput)
                System.out.println("Got data: " + data.getClass().getName());

            // if the data is a deck
            if (data instanceof Deck)
            {
                // LOGIC GOES HERE
                Deck deck = (Deck)data;

                if (deck.size() == 1)
                {
                    Card c = deck.getCard(0);
                    Card cAbove = FreecellFoundationPile.this.pileCards.lastElement().getCard();
                    if (cAbove.getSuit() == c.getSuit())
                    {
                        if (cAbove.getRank() + 1 == c.getRank() || (cAbove.getRank() == Card.DummyAceCards && c.getRank() == 1))
                        {
                            FreecellFoundationPile.this.add(new CardCanvas(c, cards));

                            freecell.pileChanged(false);
                        }
                        else
                        {
                            System.out.println("drop(), rejecting rank " + cAbove.getRank() + ", " + c.getRank());
                            e.dropComplete(false);
                            return;
                        }
                    }
                    else
                    {
                        System.out.println("drop(), rejecting suit");
                        e.dropComplete(false);
                        return;
                    }
                }
                else
                {
                    System.out.println("drop(), rejecting deck > 1");
                    e.dropComplete(false);
                    return;
                }
            }
            else
            {
                System.out.println("drop(), rejecting");
                e.dropComplete(false);
                return;
            }

            e.dropComplete(true);
        }
    }
}
